﻿package field{
	
	import field.Ball;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.utils.Dictionary;
	import flash.utils.Timer;
	import Main;
	
	
	public class GamePanel extends MovieClip {
		
		//TODO: changing of dimension
		/*Dimension of panel = 12x7. */
		
		static public const DIMENSION_WIDTH = 12;
		static public const DIMENSION_HEIGHT = 7;
		
		static public var tileWidth: Number = 0;
		static public var tileHeight: Number = 0;
		
		public var elements: Array = [];	//2d array of children balls
		public var elementsUnderCursor: Dictionary = new Dictionary(true);
		
		private var preventEmptyTimer: Timer = new Timer(2000);
										
		private var main: Main = null;
		
		public function GamePanel() {
			// constructor code
			addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
						
		}
		
		public function onAddedToStage(e: Event) {
			main = Main(root);
			main.panel = this;
			
			tileWidth = this.width / DIMENSION_WIDTH;
			tileHeight = this.height / DIMENSION_HEIGHT;
			
			//Making 2d-array from 1d
			fillFieldFully();
			main.level.addEventListener(Level.LEVEL_COMPLETED, 
				onLevelCompleted);
				
			preventEmptyTimer.addEventListener(TimerEvent.TIMER, preventEmpty);
			preventEmptyTimer.start();
		}
		
		public function fillFieldFully(): void {
			var position: Point = null;
						
			for ( var i: Number = 0; i < DIMENSION_HEIGHT; i++) {
				elements[i] = [];
				for ( var j: Number = 0; j < DIMENSION_WIDTH; j++ ) {
					position = getPositionByTile(j, i);
					elements[i][j] = new Ball();
					elements[i][j].x = position.x;
					elements[i][j].y = position.y;
					
					addChild(elements[i][j]);
				}
			}
		}
		public function getPositionByTile(x: Number, y: Number): Point {
			var result : Point = new Point();
			result.x = x * tileWidth + tileWidth / 2;
			result.y = y * tileHeight + tileHeight / 2;
			
			return result;
		}
		public function getElementTileX(x: Number) {
			return Math.round( (x - tileWidth / 2) / tileWidth );
		}
		
		public function getElementTileY(y: Number) {
			return Math.round( (y - tileHeight / 2) / tileHeight );
		}
		
		public function countElementsUnderCursor(): Number {
			var result: Number = 0;
			for each (var element: Element in elementsUnderCursor){
				result++;
			}
			
			return result;
		}
		public function findElementsUnderCursor(tileX: Number, tileY: Number, color: Number): void {
									
			if ( elements[tileY][tileX] is Bomb) {
				var bomb: Bomb = elements[tileY][tileX];
				for (var i: Number = Math.max(0, tileY - bomb.radius); i <= Math.min(GamePanel.DIMENSION_HEIGHT-1, tileY + bomb.radius); i++) {
					for (var j: Number = Math.max (0, tileX - bomb.radius); j <= Math.min(GamePanel.DIMENSION_WIDTH-1, tileX + bomb.radius); j++) {
						/*Где-то тут*/
						//if( elements[i][j] != null)
						if (elements[i][j] is Element) {
							if (elementsUnderCursor[ elements[i][j] ] != undefined  )
								continue;
							
							elementsUnderCursor[elements[i][j]] = elements[i][j];
							
							if (elements[i][j] is Bomb) {
								findElementsUnderCursor(j, i, color);
							}
						}
					}
				}
				return;
			}
				
			if (color != Ball(elements[tileY][tileX]).color )
				return;
				
			if ( elementsUnderCursor[elements[tileY][tileX]] != undefined)
				return;
			
			elementsUnderCursor[elements[tileY][tileX]] = elements[tileY][tileX];
			
			if (tileX > 0)
				if ( elements[tileY][tileX - 1] != null)
					if (elements[tileY][tileX - 1] is Ball)
						findElementsUnderCursor(tileX-1, tileY, color);
			if (tileX < GamePanel.DIMENSION_WIDTH - 1)
				if ( elements[tileY][tileX + 1] != null)
					if ( elements[tileY][tileX + 1] is Ball)
						findElementsUnderCursor(tileX + 1, tileY, color);
			if (tileY > 0)
				if ( elements[tileY - 1][tileX] != null)
					if ( elements[tileY - 1][tileX] is Ball)
						findElementsUnderCursor(tileX, tileY - 1, color);
			if (tileY < GamePanel.DIMENSION_HEIGHT - 1)
				if ( elements[tileY + 1][tileX] != null)
					if ( elements[tileY + 1][tileX] is Ball)
						findElementsUnderCursor(tileX, tileY + 1, color);
		}
		
		public function blinkElementsUnderCursor(): void {
			var length: Number = 0;
			for each(var element: Element in elementsUnderCursor) {
				length ++;
			}
			
			if (length > 1 || main.currentBonus != null) {
				for each(var element: Element in elementsUnderCursor) {
					element.blink();
				}	
			}
		}
		
		/*Stopping all blinking */
		public function stopBlink() {
			for each (var element: Element in elementsUnderCursor) {
				element.stopBlink();
			}
			elementsUnderCursor = new Dictionary;
		}
		
		public function disappearBallsUnderCursor(): Boolean {
			var needFall: Boolean = false;
			var ballCount: Number = countElementsUnderCursor();
			
			if (ballCount < 2 && main.currentBonus == null )
				return false;
			
			Main(root).scores.scores += ballCount * ballCount * 10;
			
			for each (var element: Element in elementsUnderCursor) {
				element.disableMouseControl();
				element.buttonMode = false;
				element.disappear();
				
				if (element.tileY > 0)
					if (elements[ element.tileY - 1] [element.tileX] != null)
						if ( elementsUnderCursor[ elements [element.tileY - 1] [element.tileX] ] == undefined )
								needFall = true;
					
				elements[element.tileY][element.tileX] = null;
			}
			elementsUnderCursor = new Dictionary;
			
			if(needFall)
				fallElements();
			else {
				updateTileMapAfterFalling();
				enableMouseControl();
				if (needDisplace() ) {
					displaceElements();
				}
			}
			
			return true;
		}
		
		private function preventEmpty(e: TimerEvent):void 
		{
			if (isEmpty()) {
				generateNewElements();
			}
		}
		
		private function isEmpty(): Boolean {
			for (var i: Number = 0; i < numChildren; i++) {
				if (getChildAt(i) is Element)
					return false;
			}
			return true;
		}
		
		public function enableMouseControl(): void {
			for (var ball: Number = 0; ball < numChildren; ball++) {
				if (getChildAt(ball) is Element) {
					if( ! Element(getChildAt(ball)).isDisappearing () )
						Element(getChildAt(ball)).enableMouseControl();
				}
			}
		}
		
		public function disableMouseControl(): void {
			for (var ball: Number = 0; ball < numChildren; ball++) {
				if (getChildAt(ball) is Element) {
					Element(getChildAt(ball)).disableMouseControl();
				}
			}
		}
		
		public function fallElements(): void {
			/*Gets count of free cells under ball. It needs for falling */
			function numEmptyCellsUnderBall(ballX: Number, ballY: Number): Number {
				var result: Number = 0;
				
				for (var i: Number = ballY; i < DIMENSION_HEIGHT; i++) {
					
					if ( ! (elements[i][ballX] is Element) ){
						result ++;
					}
				}
				return result;
			}
		
			disableMouseControl();
			
			/*Refilling array of balls */
			for (var i: Number = 0; i < numChildren; i++) {
				if ( getChildAt(i) is Element) {
					var element: Element = Element(getChildAt(i));
					if (element.isDisappearing () )
						continue;
						
					element.nextPosition.y = element.y + numEmptyCellsUnderBall(element.tileX, element.tileY) * tileHeight;
					if (element.nextPosition.y != element.y){
						numFallingElements++;
						element.isFalling = true;
					}
				}
			}
		}
		
		public function displaceElements(): void {
			function displaceColumn(column: Number, length: Number) {
				var element: Element = null;
				for (var i: Number = 0; i < DIMENSION_HEIGHT; i ++) {
					element = elements[i][column];
					
					if ( element == null)
						continue;
					numDisplacementingElements ++;
					
					element.isDisplacing = true;
					element.nextPosition.x = element.x + tileWidth * length;
				}
			}
			
			disableMouseControl();
			var displacement: Number = 0;
			for (var j: Number = DIMENSION_WIDTH - 1; j >= 0; j --) {
				if (elements[DIMENSION_HEIGHT - 1][j] == null){
					displacement++;
					continue;
				}
				
				displaceColumn(j, displacement);
			}
		}
		private function needDisplace(): Boolean {
			for (var j: Number = 0; j < DIMENSION_WIDTH; j++) {
				if (elements[DIMENSION_HEIGHT - 1][j] == null) 
					return true;
			}
			return false;
		}
		
		private var numFallingElements: Number = 0;
		function elementFinishedFall() {
			numFallingElements --;
			
			if (numFallingElements == 0) {
				updateTileMapAfterFalling();
				if (needDisplace() )
					displaceElements();
				else
					enableMouseControl();
			}
		}
		
		private var numDisplacementingElements: Number = 0;
		function elementFinishedDisplace() {
			numDisplacementingElements --;
			if ( numDisplacementingElements == 0) {
				updateTileMapAfterDisplacement();
				generateNewElements();
				enableMouseControl();
				//TODO: Update tiles
			}
		}
	
		private function updateTileMapAfterFalling(): void {
		
			function fallColumn(from: Number, count: Number, column: Number):void 
			{
				for (var i: Number = from; i >= 0; i--) {
					elements[i + count][column] = elements[i][column];
					elements[i][column] = null;
				}
			}
			
			//printTileMap();
			
			//Updating after fall
			for ( var j: Number = 0; j < DIMENSION_WIDTH; j++) {
				var numEmptyCell: Number = 0;
				var startY: Number = 0;
				for (var i: Number = DIMENSION_HEIGHT - 1; i >= 0; i--) {
					if ( (elements[i][j] == null ) ) {
						if (numEmptyCell == 0)
							startY = i;
						numEmptyCell ++;
					}
					else{
						if (numEmptyCell > 0) {
							fallColumn(i, numEmptyCell, j);
							numEmptyCell = 0;
							i = startY + 1;
						}
					}
					
				}
			}
			
			updateElements();
			//printTileMap();
			
		}		
		
		private function updateTileMapAfterDisplacement(): void {
			function displaceColumn(column: Number, length: Number): void {
				if (length == 0)
					return;
				for ( var i: Number = 0; i < DIMENSION_HEIGHT; i++) {
					elements[i][column + length] = elements[i][column];
					elements[i][column] = null;
				}
			}
			
			var displacement: Number = 0;
			for (var j: Number = DIMENSION_WIDTH - 1; j >= 0 ; j --) {
				if (elements[DIMENSION_HEIGHT - 1][j] == null) {
					displacement ++;
				}else {
					displaceColumn(j, displacement);
				}
			}
			
			updateElements();
			//printTileMap();
		}
		/*Updating information within elements about tile position
		 * after tile map changing*/
		function updateElements() {
			for (var i: Number = 0; i < DIMENSION_HEIGHT; i++) {
				for (var j: Number = 0; j < DIMENSION_WIDTH; j++) {
					if (elements[i][j] != null){
						elements[i][j].tileX = j;
						elements[i][j].tileY = i;
					}
				}
			}
		}
		
		function printTileMap():void 
		{
			
			for (var i: Number = 0; i < elements.length; i++) {
				var s: String = "";
				for ( var j: Number = 0; j < elements[i].length; j++) {
					if (elements[i][j] is Ball){
						s += "O";
					}else if (elements[i][j] is Bomb) {
						s += "X";
					}else s += " ";
				}
				trace (s);
			}
		}
		
		function generateNewElements() {
			function generateNewColumn(column: Number): void {
				var position: Point = new Point();
				for ( var i: Number = 0; i < DIMENSION_HEIGHT; i++) {
					position = getPositionByTile(column, i);
					
					var random: Number = Math.random() * 100;
					if ( random < 95)
						elements[i][column] = new Ball();
					else if ( random < 99)
						elements[i][column] = new NormalBomb();
					else
						elements[i][column] = new AtomicBomb();
					elements[i][column].x = position.x;
					elements[i][column].y = position.y;
					elements[i][column].tileX = column;
					elements[i][column].tileY = i;
					
					addChild(elements[i][column]);
				}
			}
			
			for (var j: Number = 0; j < DIMENSION_WIDTH; j ++) {
				if (elements[DIMENSION_HEIGHT - 1][j] == null)
					generateNewColumn(j);
			}
		}
		
		public function onLevelCompleted(e: Event) {
			//TODO: animation
			var ball: Ball = null;
			var position: Point = null;
			for (var j: Number = 0; j < DIMENSION_WIDTH; j++) {
				for (var i: Number = 0; i < DIMENSION_HEIGHT; i++) {
					if (elements[i][j] == null){
						if (i < DIMENSION_HEIGHT - 1){
							if (elements[i + 1][j] != null){
								ball = new Ball();
								
								ball.tileX = j; 
								ball.tileY = i;
								
								position = getPositionByTile(ball.tileX, 
									ball.tileY);
								ball.x = position.x;
								ball.y = position.y;
								
								elements[ball.tileY][ball.tileX] = ball;
								
								ball.visible = true;
								ball.gotoAndPlay("nextLevel");
								addChild(ball);
							}
						}//TODO: appearing on bare floor
					}
				}
			}
		}
	}
	
	
}
